/**
 * Title : jFamilyTree
 * Description: jFamilyTree
 * About: Designed and rewritten by Andreas Lundgren, based on the Treebolic application suite
 * Copyright:	(c) 2001-2008
 * Terms of use:see license agreement at http://treebolic.sourceforge.net/en/license.htm
 * Author: Bernard Bou
 * Company: bsys
 */
package jFamilyTree.commons;

import java.awt.Dimension;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.Properties;
import java.util.StringTokenizer;
import java.util.Vector;

import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.table.AbstractTableModel;

/**
 * URL dialog
 * 
 * @author Bernard Bou
 */
public class UrlDialog extends JDialog
{
	private static final long serialVersionUID = 1L;

	// V A L U E S

	/**
	 * Properties (input/output)
	 */
	private Properties theProperties;

	/**
	 * Ok flag
	 */
	public boolean ok;

	// C O M P O N E N T S

	/**
	 * URL
	 */
	private JTextField theUrlTextField;

	/**
	 * Parameter table
	 */
	private JTable theQueryTable;

	/**
	 * Constructor
	 */
	public UrlDialog(Properties theseProperties)
	{
		super();
		theProperties = theseProperties;
		initialize();
	}

	/**
	 * Initialize
	 */
	private void initialize()
	{
		setTitle(Messages.getString("UrlDialog.0")); //$NON-NLS-1$
		setResizable(true);

		// label
		JLabel thisUrlLabel = new JLabel(Messages.getString("UrlDialog.1")); //$NON-NLS-1$
		thisUrlLabel.setToolTipText(Messages.getString("UrlDialog.2")); //$NON-NLS-1$
		JLabel thisQueryLabel = new JLabel(Messages.getString("UrlDialog.3")); //$NON-NLS-1$
		thisQueryLabel.setToolTipText(Messages.getString("UrlDialog.4")); //$NON-NLS-1$

		// url
		theUrlTextField = new JTextField();
		theUrlTextField.setFont(new Font("Dialog", 0, 12)); //$NON-NLS-1$
		theUrlTextField.setToolTipText(Messages.getString("UrlDialog.6")); //$NON-NLS-1$

		// parameter table
		theQueryTable = new JTable();
		theQueryTable.setModel(new ParameterModel(null));
		theQueryTable.getColumnModel().getColumn(0).setMaxWidth(90);
		theQueryTable.setToolTipText(Messages.getString("UrlDialog.5")); //$NON-NLS-1$
		JScrollPane thisScrollPane = new JScrollPane(theQueryTable);
		thisScrollPane.setPreferredSize(new Dimension(0, 60));
		
		// buttons
		JButton thisOkButton = new JButton(Messages.getString("UrlDialog.10")); //$NON-NLS-1$
		thisOkButton.addActionListener(new java.awt.event.ActionListener()
		{
			/*
			 * (non-Javadoc)
			 * 
			 * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
			 */
			public void actionPerformed(ActionEvent e)
			{
				ok = true;
				setVisible(false);
			}
		});
		JButton thisCancelButton = new JButton(Messages.getString("UrlDialog.11")); //$NON-NLS-1$
		thisCancelButton.addActionListener(new java.awt.event.ActionListener()
		{
			/*
			 * (non-Javadoc)
			 * 
			 * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
			 */
			public void actionPerformed(ActionEvent e)
			{
				setVisible(false);
			}
		});
		JButton thisAddParameter = new JButton(Messages.getString("UrlDialog.15")); //$NON-NLS-1$
		thisAddParameter.addActionListener(new java.awt.event.ActionListener()
		{
			/*
			 * (non-Javadoc)
			 * 
			 * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
			 */
			public void actionPerformed(ActionEvent e)
			{
				ParameterModel thisModel = (ParameterModel) theQueryTable.getModel();
				if (thisModel != null)
					thisModel.newRow(null);
			}
		});
		JButton thisDeleteParameter = new JButton(Messages.getString("UrlDialog.16")); //$NON-NLS-1$
		thisDeleteParameter.addActionListener(new java.awt.event.ActionListener()
		{
			/*
			 * (non-Javadoc)
			 * 
			 * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
			 */
			public void actionPerformed(ActionEvent e)
			{
				int thisRowIdx = theQueryTable.getSelectedRow();
				ParameterModel thisModel = (ParameterModel) theQueryTable.getModel();
				if (thisRowIdx != -1 && thisModel != null)
					thisModel.deleteRow(thisRowIdx);
			}
		});

		// button panel
		JPanel thisButtonPanel = new JPanel();
		thisButtonPanel.add(thisAddParameter, null);
		thisButtonPanel.add(thisDeleteParameter, null);
		thisButtonPanel.add(thisCancelButton, null);
		thisButtonPanel.add(thisOkButton, null);

		// assemble
		JPanel thisPanel = new JPanel();
		thisPanel.setLayout(new GridBagLayout());
		thisPanel.add(thisUrlLabel, new GridBagConstraints(0, 1, 1, 1, 0., 0., GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(10, 10, 0, 10), 0, 0));
		thisPanel.add(theUrlTextField, new GridBagConstraints(0, 2, 1, 1, 0., 0., GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(0, 10, 10, 10), 0, 0));
		thisPanel.add(thisQueryLabel, new GridBagConstraints(0, 3, 1, 1, 0., 0., GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 10, 0, 10), 0, 0));
		thisPanel.add(thisScrollPane, new GridBagConstraints(0, 4, 1, 1, 0., 0., GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(0, 10, 0, 10), 0, 0));
		thisPanel.add(thisButtonPanel, new GridBagConstraints(0, 5, 1, 1, 0., 0., GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0));

		setContentPane(thisPanel);
	}

	// A C C E S S

	/**
	 * Make url end point
	 * 
	 * @return url end point
	 */
	private String getURL(String thisUrl, String thisQuery)
	{
		String thisResult = thisUrl;
		if (thisQuery != null)
		{
			thisResult += "?"; //$NON-NLS-1$
			thisResult += thisQuery;
		}
		return thisResult;
	}

	/**
	 * Make query
	 * 
	 * @return stringified query
	 */
	private String getQuery(ParameterModel thisModel)
	{
		if (thisModel != null)
		{
			Properties theseParameters = thisModel.getProperties();
			if (theseParameters != null && theseParameters.size() != 0)
				return properties2Query(theseParameters);
		}
		return null;
	}

	// P A R A M S . H E L P E R S

	/**
	 * Convert query to properties
	 * 
	 * @param thisQuery
	 *        queries
	 * @return property set
	 */
	static private Properties query2Properties(String thisQuery)
	{
		Properties theseProperties = new Properties();
		StringTokenizer thisStringTokenizer = new StringTokenizer(thisQuery, "&"); //$NON-NLS-1$
		while (thisStringTokenizer.hasMoreTokens())
		{
			StringTokenizer thisStringTokenizer2 = new StringTokenizer(thisStringTokenizer.nextToken(), "="); //$NON-NLS-1$
			if (thisStringTokenizer2.countTokens() != 2)
				continue;

			String thisName = thisStringTokenizer2.nextToken();
			String thisValue = decode(thisStringTokenizer2.nextToken());
			theseProperties.put(thisName, thisValue);
		}
		return theseProperties;
	}

	/**
	 * Convert properties to query
	 * 
	 * @param theseParams
	 *        properties
	 * @return query string
	 */
	static private String properties2Query(Properties theseParams)
	{
		if (theseParams == null)
			return null;

		String thisQuery = ""; //$NON-NLS-1$
		int i = 0;
		for (String thisKey : theseParams.stringPropertyNames())
		{
			String thisValue = theseParams.getProperty(thisKey);
			if (i++ != 0)
				thisQuery += "&"; //$NON-NLS-1$
			thisQuery += thisKey + "=" + encode(thisValue); //$NON-NLS-1$
		}
		return thisQuery;
	}

	// D E C O D E / E N C O D E

	/**
	 * Decode encoded URL (for display)
	 * 
	 * @param thisString
	 *        encode URL string
	 * @return decoded URL string
	 */
	static private String decode(String thisString)
	{
		try
		{
			return URLDecoder.decode(thisString, "UTF8"); //$NON-NLS-1$
		}
		catch (UnsupportedEncodingException e)
		{
			System.err.println(Messages.getString("UrlDialog.110") + thisString + " - " + e); //$NON-NLS-1$ //$NON-NLS-2$
		}
		return thisString;
	}

	/**
	 * Encode encoded URL
	 * 
	 * @param thisString
	 *        encode URL string
	 * @return decoded URL string
	 */
	static private String encode(String thisString)
	{
		try
		{
			return URLEncoder.encode(thisString, "UTF8"); //$NON-NLS-1$
		}
		catch (UnsupportedEncodingException e)
		{
			System.err.println(Messages.getString("UrlDialog.110") + thisString + " - " + e); //$NON-NLS-1$ //$NON-NLS-2$
		}
		return thisString;
	}

	// M O D E L

	/**
	 * Parameter model
	 * 
	 * @author Bernard Bou
	 */
	private class ParameterModel extends AbstractTableModel
	{
		/**
		 * Serial version uid
		 */
		private static final long serialVersionUID = 1L;

		/**
		 * Properties
		 */
		private Vector<Entry> theEntries = new Vector<Entry>();

		private class Entry
		{
			/**
			 * Key
			 */
			public String theKey;

			/**
			 * Value
			 */
			public String theValue;

			/**
			 * Constructor
			 */
			public Entry()
			{
				theKey = null;
				theValue = null;
			}

			/**
			 * Constructor
			 * 
			 * @param thisKey
			 *        key
			 * @param thisValue
			 *        value
			 */
			public Entry(String thisKey, String thisValue)
			{
				theKey = thisKey;
				theValue = thisValue;
			}
		}

		/**
		 * Parameter model
		 * 
		 * @param theseParams
		 *        parameters
		 */
		public ParameterModel(Properties theseParams)
		{
			if (theseParams == null)
				newRow(null);
			else
			{
				for (Object thisKey : theseParams.keySet())
				{
					Object thisValue = theseParams.get(thisKey);
					newRow(new ParameterModel.Entry((String) thisKey, (String) thisValue));
				}
			}
		}

		/**
		 * New row
		 * 
		 * @param thisEntry
		 *        entry
		 * @return row index
		 */
		public int newRow(Entry thisEntry)
		{
			theEntries.add(thisEntry != null ? thisEntry : new Entry());
			int thisRowIdx = theEntries.size() - 1;
			this.fireTableRowsInserted(thisRowIdx, thisRowIdx);
			return thisRowIdx;
		}

		/**
		 * Delete row
		 * 
		 * @param thisRowIdx
		 *        row index
		 */
		public void deleteRow(int thisRowIdx)
		{
			if (thisRowIdx >= theEntries.size())
			{
				return;
			}
			theEntries.remove(thisRowIdx);
			this.fireTableRowsDeleted(thisRowIdx, thisRowIdx);
		}

		/**
		 * Get properties
		 * 
		 * @return properties
		 */
		public Properties getProperties()
		{
			Properties theseProperties = null;
			for (Entry thisEntry : theEntries)
			{
				if (thisEntry.theKey != null && !thisEntry.theKey.isEmpty() && thisEntry.theValue != null && !thisEntry.theValue.isEmpty())
				{
					if (theseProperties == null)
					{
						theseProperties = new Properties();
					}
					theseProperties.put(thisEntry.theKey, thisEntry.theValue);
				}
			}
			return theseProperties;
		}

		/*
		 * (non-Javadoc)
		 * 
		 * @see javax.swing.table.TableModel#getRowCount()
		 */
		public int getRowCount()
		{
			return theEntries.size();
		}

		/*
		 * (non-Javadoc)
		 * 
		 * @see javax.swing.table.TableModel#getColumnCount()
		 */
		public int getColumnCount()
		{
			return 2;
		}

		/*
		 * (non-Javadoc)
		 * 
		 * @see javax.swing.table.TableModel#isCellEditable(int, int)
		 */
		public boolean isCellEditable(int y, int x)
		{
			return true;
		}

		/*
		 * (non-Javadoc)
		 * 
		 * @see javax.swing.table.TableModel#setValueAt(java.lang.Object, int, int)
		 */
		public void setValueAt(Object thisValue, int y, int x)
		{
			Entry thisEntry = theEntries.get(y);
			switch (x)
			{
			case 0:
				thisEntry.theKey = (String) thisValue;
				break;
			case 1:
				thisEntry.theValue = (String) thisValue;
				break;
			default:
				break;
			}
		}

		/*
		 * (non-Javadoc)
		 * 
		 * @see javax.swing.table.TableModel#getValueAt(int, int)
		 */
		public Object getValueAt(int y, int x)
		{
			Entry thisEntry = (Entry) theEntries.get(y);
			switch (x)
			{
			case 0:
				return thisEntry.theKey;
			case 1:
				return thisEntry.theValue;
			default:
				return null;
			}
		}

		/*
		 * (non-Javadoc)
		 * 
		 * @see javax.swing.table.TableModel#getColumnName(int)
		 */
		public String getColumnName(int x)
		{
			switch (x)
			{
			case 0:
				return Messages.getString("UrlDialog.20"); //$NON-NLS-1$
			case 1:
				return Messages.getString("UrlDialog.21"); //$NON-NLS-1$
			default:
				return ""; //$NON-NLS-1$
			}
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.awt.Dialog#setVisible(boolean)
	 */
	public void setVisible(boolean thisFlag)
	{
		if (thisFlag)
		{
			ok = false;

			// read properties into components
			String thisProperty = theProperties.getProperty("openurl"); //$NON-NLS-1$
			if (thisProperty != null)
			{
				String[] theseFields = thisProperty.split("\\?"); //$NON-NLS-1$
				String thisUrl = theseFields.length > 0 ? theseFields[0] : null;
				String thisQuery = theseFields.length > 1 ? theseFields[1] : null;
				if (thisUrl != null)
				{
					theUrlTextField.setText(thisUrl);
					theUrlTextField.selectAll();
				}
				if (thisQuery != null)
				{
					Properties theseParameters = query2Properties(thisQuery);
					ParameterModel thisModel = new ParameterModel(theseParameters);
					theQueryTable.setModel(thisModel);
					theQueryTable.getColumnModel().getColumn(0).setMaxWidth(90);
				}
			}

			pack();
			Utils.center(this);
		}
		else
		{
			if (ok)
			{
				// update properties from components
				String thisUrl = theUrlTextField.getText();
				String thisQuery = getQuery((ParameterModel) theQueryTable.getModel());
				theProperties.setProperty("openurl", getURL(thisUrl, thisQuery)); //$NON-NLS-1$
			}
			else
				theProperties.remove("openurl"); //$NON-NLS-1$
		}
		super.setVisible(thisFlag);
	}
}
